package com.unitd.modules.sso.interceptor;

import com.alibaba.fastjson.JSON;
import com.unitd.frame.comm.utils.ExceptionUtils;
import com.unitd.frame.comm.utils.IpUtils;
import com.unitd.frame.comm.utils.http.HttpUtils;
import com.unitd.frame.spring.helper.EnvironmentHelper;
import com.unitd.frame.springweb.controller.result.AjaxResult;
import com.unitd.frame.sso.common.token.SSOToken;
import com.unitd.modules.sso.util.SecurityUtil;
import com.unitd.frame.comm.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @desc 权限信息拦截器, 用于拦截登录/未登录,以及登录后的权限和API接口请求权限
 * @filename SecurityInterceptor.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2016/11/13
 */
public class SecurityInterceptor implements HandlerInterceptor {

	private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

	private static final String ADMIN_URI_PREFIX = "/admin";
	private static final String API_URI_PREFIX = "/api";
	private static List<String> ipWhiteList = new ArrayList<>();

	/** 是否允许通过外网拉取配置 */
	private boolean extranetEnabled = Boolean.parseBoolean(EnvironmentHelper.getProperty("api.extranet.enabled"));
	/** 是否启用安全ip */
	private boolean ipfilterEnabled = Boolean.parseBoolean(EnvironmentHelper.getProperty("safe.ipfilter.enabled"));

	/**
	 * @desc 处理用户请求之前需要进行拦截的操作
	 * @param request  http的request请求
	 * @param response http的response响应
	 * @param handler  请求数据对象
	 * @return true/false 成功与否
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
		try {
			// 当前请求的IP地址信息
			String ipAddr = IpUtils.getIpAddr(request);
			// 是否内网IP
			boolean isInnerIpaddr = IpUtils.isInnerIp(ipAddr);

			// 设置当前请求的日志信息
			SecurityUtil.getOperateLog().setIpAddr(ipAddr);
			SecurityUtil.getOperateLog().setActName(request.getRequestURI());

			// 客户端APi鉴权
			if (request.getRequestURI().startsWith(API_URI_PREFIX)) {
				// 只允许内网
				if (!extranetEnabled && !isInnerIpaddr) {
					AjaxResult ajaxResult = new AjaxResult(403, false, "禁止外网访问", null);
					HttpUtils.responseOutWithJson(response, JSON.toJSONString(ajaxResult));
					return false;
				}

				// 验证签名,将请求进行解密处理,判断签名是否正确
				String clientId = request.getParameter("client_id");
				String signature = request.getParameter("signature");
				logger.info("当前发起请求的客户端ID为:" + clientId + ",签名信息为:" + signature);

				return true;
			}

			if (request.getRequestURI().startsWith(ADMIN_URI_PREFIX) && ipfilterEnabled && !isInnerIpaddr && !ipWhiteList.contains(ipAddr)) {
				AjaxResult ipForbiddenRspJson = new AjaxResult(403, false, "当前访问IP未被授予访问权限,请确认!", null);
				HttpUtils.responseOutWithJson(response, JSON.toJSONString(ipForbiddenRspJson));
				return false;
			}

			SSOToken loginUserInfo = SecurityUtil.getLoginUserInfo();
			if (loginUserInfo == null) {
				if (HttpUtils.isAjax(request)) {
					AjaxResult notLoginRspJson = new AjaxResult(401, false, "401 未被授权的访问!", null);
					HttpUtils.responseOutWithJson(response, JSON.toJSONString(notLoginRspJson));
				} else {
					response.sendRedirect(request.getContextPath() + "/login.html");
				}
				return false;
			}

			// 完善当前请求的日志信息
			SecurityUtil.getOperateLog().setUid(loginUserInfo.getUid());
			SecurityUtil.getOperateLog().setUname(loginUserInfo.getUname());

			return true;
		} catch (IOException e) {
			ExceptionUtils.unchecked(e);
		}
		return false;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
						   ModelAndView modelAndView) {
	}

	/**
	 * @desc 处理完请求后需要进行的处理
	 * @param request  http的request请求
	 * @param response http的response响应
	 * @param handler  请求数据对象
	 * @param ex       处理过程中的异常
	 */
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
		// 释放当前请求操作的日志线程句柄对象, 完成日志操作
		SecurityUtil.clearOperateLogHolder();
	}

	/**
	 * @desc 设置请求过滤的白名单
	 * @param ips 白名单ip串
	 */
	public static synchronized void setIpWhiteList(String ips) {
		ipWhiteList.clear();
		if (StringUtils.isBlank(ips)) return;
		ipWhiteList.addAll(Arrays.asList(ips.split(",|;|，")));
	}
}